/*-------------------------------------------------------------------------
 - Copyright (c) 2024-2025 [XD-AMCC TEAM]
 - [XD-AMCC] is licensed under Mulan PSL v2.
 - You can use this software according to the terms and conditions of the Mulan PSL v2.
 - You may obtain a copy of Mulan PSL v2 at:
 -          http://license.coscl.org.cn/MulanPSL2
 - THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES
 - OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
 - TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 - See the Mulan PSL v2 for more details.
 ------------------------------------------------------------------------*/


/**
 * @brief  Semantic actions of statements for building symbol tables.
 *
 * @author  WXQ#XDU
 * @date    2024.12
 */

#include "_stb_inner.h"


// pre-semantic-action for labeledStatement:
//   Creates the symbol entry for label and stores it into the
//   enclosing function's symbol table.
//
void stb_enter_labeledStatement(t_tree_listener_ptr listenerPtr, t_tree_ptr node) {
    t_symtable_builder * builderPtr = (t_symtable_builder *)listenerPtr;
    t_tree_ptr   labelNode = tree_get_child(node, 0); // store definition of label
    t_token_ptr   tokenPtr = tree_get_token(labelNode); // label's token
    t_position_ptr     pos = token_get_position(tokenPtr);

    t_stack_item_ptr   stackItemPtr = SCOPE_STACK_TOP(builderPtr);
    t_func_symInfo_ptr pFuncSymInfo = stackItemPtr->currFuncInfoPtr->funcSymInfoPtr;
    if (NULL == pFuncSymInfo) {
        ERROR_REPORT_INPUT(pos->filePath, pos->line, pos->line,
                           EC_LABEL_BADPOS, token_get_text(tokenPtr));
        return;
    }

    t_symentry_ptr pSymEntry = symtable_putLabelDefine(
                        pFuncSymInfo->symTablePtr,
                        labelNode);
    if (NULL != pSymEntry) {
        LOG_STEP ("put symENTRY %tx of label \"%s\" into "
                  "symTABLE %tx of function %s",
                  pSymEntry,    token_get_text(tokenPtr),
                  pFuncSymInfo->symTablePtr,
                  stackItemPtr->currFuncInfoPtr->funcName);
    } else {
        log_error("failed to record symentry of label \"%s\" into "
                  "symTABLE %tx of function %s",
                  token_get_text(tokenPtr),
                  pFuncSymInfo->symTablePtr,
                  stackItemPtr->currFuncInfoPtr->funcName);
    }
}


// pre-semantic-action for compoundStatement:
//   Creates its symbol table that stores local names;
//   PUSH this table onto the scope stack.
//
void stb_enter_compoundStatement(t_tree_listener_ptr listenerPtr, t_tree_ptr node) {
    t_symtable_builder * builderPtr = (t_symtable_builder *)listenerPtr;
    t_nonterminal_ptr ntPtr = tree_get_nonTerminal( tree_get_parent(node) );

    // function-body's symbol table is the function's table
    if ( NT_funcDeclaration == ntPtr->ntId )
        return ;

    t_position  pos1 = tree_get_startPosition(node);
    t_position  pos2 = tree_get_stopPosition(node);
    log_trace("CREATing symTABLE of scope NT-compoundStatement"
              ", tree-node=%tx, pos-range=[%d:%d , %d:%d]",
              node,       pos1.line,   pos1.column,
              pos2.line,  pos2.column);

    t_stack_item_ptr         stackItemPtr = SCOPE_STACK_TOP(builderPtr);
    t_symtable_ptr enclosingScopeSymTable = stackItemPtr->symtablePtr;
    t_symtable_ptr pSymbolTable = symtable_new(node, enclosingScopeSymTable);
    list_append(&(enclosingScopeSymTable->blockTables), pSymbolTable );

    // the stack item is bound with the current function info.
    SCOPE_STACK_PUSH(builderPtr, pSymbolTable, stackItemPtr->currFuncInfoPtr);
    LOG_STEP ("SCOPE-STACK: PUSH symTABLE %tx of scope NT-compoundStatement"
              ", tree-node=%tx, pos-range=[%d:%d , %d:%d]",
              pSymbolTable,  node,
              pos1.line,  pos1.column,
              pos2.line,  pos2.column);
}


// post-semantic-action for compoundStatement:
//   POP local symbol-table from scope stack.
//
void stb_exit_compoundStatement(t_tree_listener_ptr listenerPtr, t_tree_ptr node) {
    t_symtable_builder * builderPtr = (t_symtable_builder *)listenerPtr;
    t_nonterminal_ptr ntPtr = tree_get_nonTerminal( tree_get_parent(node) );

    // function-body's symbol table is the function's
    if ( NT_funcDeclaration == ntPtr->ntId )
        return ;

    t_symtable_ptr pSymbolTable = SCOPE_STACK_TOP(builderPtr)->symtablePtr;
    SCOPE_STACK_POP(builderPtr);

    t_position  pos1 = tree_get_startPosition(node);
    t_position  pos2 = tree_get_stopPosition(node);
    LOG_STEP ("SCOPE-STACK: POP symTABLE %tx of scope NT-compoundStatement"
              ", tree-node=%tx, pos-range=[%d:%d , %d:%d]",
              pSymbolTable, node,
              pos1.line,  pos1.column,
              pos2.line,  pos2.column);
}


// post-semantic-action for jumpStatement:
//   Resolve the reference to a label (in goto-stmt).
//   (1) A label name may be used before its definition;
//   (2) This name is the ID in the following structure (production):
//       jumpStatement-> GOTO ID  ';'
//
void stb_exit_jumpStatement(t_tree_listener_ptr listenerPtr, t_tree_ptr node) {
    t_symtable_builder * builderPtr = (t_symtable_builder *) listenerPtr;
    t_tree_ptr     pIdNode      = tree_get_child ( node, 0 );
    t_token_ptr    pIdToken     = tree_get_token ( pIdNode );
    if ( TK_KW_GOTO != token_get_kind(pIdToken) )
        return;

    pIdNode      = tree_get_child ( node, 1 );
    pIdToken     = tree_get_token ( pIdNode );
    t_position_ptr pIdPosition  = token_get_position(pIdToken);

    // a label is stored in the symbol table of enclosing function
    t_funcinfo_ptr  funcinfoPtr = SCOPE_STACK_TOP(builderPtr)->currFuncInfoPtr;
    t_symtable_ptr pSymbolTable = funcinfoPtr->symTablePtr;
    t_symentry_ptr pSymEntry    = symtable_find_local(pSymbolTable, pIdNode);
    if (NULL != pSymEntry) {
        // this reference to a label is after its definition
        stb_bindLabelReferToDef(pIdNode, pSymEntry);
        return;
    }

    // make one chain of undefined labels in current function,
    // and try binding later in exit_functionDefinition()
    list_append(&(funcinfoPtr->undefinedLabels), pIdNode);
    LOG_STEP ("*** records a unDECLARed LABEL \"%s\" at [%d:%d]",
              token_get_text(pIdToken),
              pIdPosition->line, pIdPosition->column);
}

